
// import haxe.ds.Array;
// import oimo.m.Vec3;
// import oimo.m.M;
// import oimo.common.Transform;
// import oimo.common.Vec3;

import { ConvexGeometry } from "./ConvexGeometry";
import { Vec3 } from "../../common/Vec3";
import { GeometryType } from "./GeometryType";
import { M } from "../../common/M";
import { Aabb } from "./Aabb";
import { Transform } from "../../common/Transform";

/**
 * A convex hull collision geometry. A convex hull of the vertices is the smallest convex
 * polyhedron which contains all vertices.
 */

export class ConvexHullGeometry extends ConvexGeometry {
	_vertices: Array<Vec3>;
	_tmpVertices: Array<Vec3>; // for internal use
	_numVertices: number;

	/**
	 * Creates a convex hull collision geometry of the vertices `vertices`.
	 */
	constructor(vertices: Array<Vec3>) {
		super(GeometryType._CONVEX_HULL);
		this._numVertices = vertices.length;
		this._vertices = new Array<Vec3>(this._numVertices);
		this._tmpVertices = new Array<Vec3>(this._numVertices);
		for (let i = 0; i < this._numVertices; ++i) {
			this._vertices[i] = vertices[i];
			this._tmpVertices[i] = new Vec3();
		}
		this._useGjkRayCast = true;
		this._updateMass();
	}

	/**
	 * Returns the vertices of the convex hull.
	 */
	public getVertices(): Array<Vec3> {
		return this._vertices;
	}

	public _updateMass(): void {
		this._volume = 1;
		M.mat3_diagonal(this._inertiaCoeff, 1, 1, 1);

		var minx: number = this._vertices[0].x;
		var miny: number = this._vertices[0].y;
		var minz: number = this._vertices[0].z;
		var maxx: number = this._vertices[0].x;
		var maxy: number = this._vertices[0].y;
		var maxz: number = this._vertices[0].z;

		for (let i = 1; i < this._numVertices; ++i) {
			var vx: number = this._vertices[i].x;
			var vy: number = this._vertices[i].y;
			var vz: number = this._vertices[i].z;
			if (vx < minx) minx = vx;
			else if (vx > maxx) maxx = vx;
			if (vy < miny) miny = vy;
			else if (vy > maxy) maxy = vy;
			if (vz < minz) minz = vz;
			else if (vz > maxz) maxz = vz;
		}

		var sizex: number = maxx - minx;
		var sizey: number = maxy - miny;
		var sizez: number = maxz - minz;
		this._volume = sizex * sizey * sizez;
		var diffCog: number = (
			(minx + maxx) * (minx + maxx) +
			(miny + maxy) * (miny + maxy) +
			(minz + maxz) * (minz + maxz)
		) * 0.25;

		// (size / 2) ^ 2
		sizex = sizex * sizex * 0.25;
		sizey = sizey * sizey * 0.25;
		sizez = sizez * sizez * 0.25;


		M.mat3_diagonal(this._inertiaCoeff,
			1 / 3 * (sizey + sizez) + diffCog,
			1 / 3 * (sizez + sizex) + diffCog,
			1 / 3 * (sizex + sizey) + diffCog
		);
	}

	public _computeAabb(aabb: Aabb, tf: Transform): void {
		var min: Vec3 = new Vec3();
		var max: Vec3 = new Vec3();
		var margin: Vec3 = new Vec3();

		M.vec3_set(margin, this._gjkMargin, this._gjkMargin, this._gjkMargin);

		var localV: Vec3 = new Vec3();
		M.vec3_fromVec3(localV, this._vertices[0]);
		var worldV: Vec3 = new Vec3();
		M.vec3_mulMat3(worldV, localV, tf._rotation);
		M.vec3_add(worldV, worldV, tf._position);

		M.vec3_assign(min, worldV);
		M.vec3_assign(max, worldV);

		for (let i = 1; i < this._numVertices; ++i) {
			M.vec3_fromVec3(localV, this._vertices[i]);
			M.vec3_mulMat3(worldV, localV, tf._rotation);
			M.vec3_add(worldV, worldV, tf._position);
			M.vec3_min(min, min, worldV);
			M.vec3_max(max, max, worldV);
		}

		M.vec3_sub(aabb._min, min, margin);
		M.vec3_add(aabb._max, max, margin);
	}

	public computeLocalSupportingVertex(dir: Vec3, out: Vec3): void {
		var maxDot: number = this._vertices[0].dot(dir);
		var maxIndex: number = 0;
		for (let i = 1; i < this._numVertices; ++i) {
			var dot: number = this._vertices[i].dot(dir);
			if (dot > maxDot) {
				maxDot = dot;
				maxIndex = i;
			}
		}
		out.copyFrom(this._vertices[maxIndex]);
	}

}
